<template>
  <component
    ref="formEl"
    :is="formRegister ? 'w-form-element' : 'div'"
    v-bind="
      formRegister && { validators, inputValue: isOn, disabled, readonly }
    "
    v-model:valid="valid"
    @reset="
      $emit('update:modelValue', (isOn = null));
      $emit('input', null);
    "
    :class="classes"
    ><input
      ref="input"
      :id="`w-switch--${_.uid}`"
      type="checkbox"
      :name="inputName"
      :checked="isOn"
      :disabled="disabled || readonly || null"
      :readonly="readonly || null"
      :aria-readonly="readonly ? 'true' : 'false'"
      :required="required || null"
      @change="
        onInput() /* Edge doesn't fire input on checkbox/radio/select change */
      "
      @focus="$emit('focus', $event)"
      :aria-checked="isOn || 'false'"
      role="switch"
    /><template v-if="hasLabel && labelOnLeft"
      ><label
        class="w-switch__label w-form-el-shakable"
        v-if="$slots.default"
        :for="`w-switch--${_.uid}`"
      >
        <slot></slot> </label
      ><label
        class="w-switch__label w-form-el-shakable"
        v-else-if="label"
        :for="`w-switch--${_.uid}`"
        v-html="label"
      ></label
    ></template>
    <div
      class="w-switch__input"
      @click="
        $refs.input.focus();
        $refs.input.click();
      "
      :class="[[this.color], hasLabel ? (thin ? 'mr3' : 'mr2') : '']"
    ></div>
    <template v-if="hasLabel && !labelOnLeft"
      ><label
        class="w-switch__label w-form-el-shakable"
        v-if="$slots.default"
        :for="`w-switch--${_.uid}`"
      >
        <slot></slot> </label
      ><label
        class="w-switch__label w-form-el-shakable"
        v-else-if="label"
        :for="`w-switch--${_.uid}`"
        v-html="label"
      ></label
    ></template>
  </component>
</template>

<script>
import { ref, reactive, computed, watch, getCurrentInstance } from "vue";
export default {
  name: "w-switch",
  inject: { formRegister: { default: null } },

  props: {
    modelValue: { default: false }, // v-model.
    label: { type: String, default: "" },
    labelOnLeft: { type: Boolean },
    color: { type: String, default: "primary" },
    thin: { type: Boolean },
    noRipple: { type: Boolean },
    // Also name, disabled, readonly, required and validators in the mixin.
    name: { type: String }, // When sending data through form.
    disabled: { type: Boolean },
    readonly: { type: Boolean },
    required: { type: Boolean },
    validators: { type: Array }
  },

  emits: ["input", "update:modelValue", "focus"],
  setup(props, ctx) {
    const instance = getCurrentInstance();
    const isOn = ref(props.modelValue);
    const ripple = reactive({
      start: false,
      end: false,
      timeout: null
    });
    const valid = ref(null);

    const hasLabel = computed(() => {
      return props.label || ctx.slots.default;
    });
    const classes = computed(() => {
      return {
        [`w-switch w-switch--${isOn.value ? "on" : "off"}`]: true,
        "w-switch--thin": props.thin,
        "w-switch--disabled": props.disabled,
        "w-switch--readonly": props.readonly,
        "w-switch--ripple": ripple.start,
        "w-switch--rippled": ripple.end
      };
    });

    const inputName = computed(() => {
      return (
        props.name || `${instance.type.name}--${instance.type.uid || instance.uid}`
      );
    });

    function onInput() {
      isOn.value = !isOn.value;
      ctx.emit("update:modelValue", isOn.value);
      ctx.emit("input", isOn.value);

      if (!props.noRipple) {
        if (isOn.value) {
          ripple.start = true;
          ripple.timeout = setTimeout(() => {
            ripple.start = false;
            ripple.end = true;
            setTimeout(() => (ripple.end = false), 100);
          }, 700);
        } else {
          ripple.start = false;
          clearTimeout(ripple.timeout);
        }
      }
    }

    watch(
      () => props.modelValue,
      value => {
        isOn.value = value;
      }
    );
    return {
      isOn,
      ripple,
      valid,
      hasLabel,
      classes,
      inputName,
      // methods
      onInput
    };
  }
};
</script>